/*******************************************************************************
 * Copyright (c) 2002, 2003 David Corbin and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Common Public License v0.5
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v05.html
 * 
 * Contributors:
 *    David Corbin - Initial implementation.
 *    Boris Pruessmann - Some cleanup
 *    Rouven Froeleke - Base implementation of moveFolder
 ******************************************************************************/
package net.sourceforge.perforce.core.internal;

import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import net.sourceforge.perforce.core.IP4TeamProvider;
import net.sourceforge.perforce.core.PerforceCorePlugin;
import net.sourceforge.perforce.core.PerforceException;
import net.sourceforge.perforce.core.PerforceSynchronizer;
import net.sourceforge.perforce.core.api.IPerforceServer;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceVisitor;
import org.eclipse.core.resources.team.IMoveDeleteHook;
import org.eclipse.core.resources.team.IResourceTree;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.team.core.RepositoryProvider;


/** Implements IMoveDeleteHook, so refactoring of classes in source control will
  * work correctly.
  * 
  * @see org.eclipse.core.resources.team.IMoveDeleteHook
  * 
  * @author <a href="mailto:dcorbin@users.sourceforge.net">David Corbin</a>
  * @author <a href="mailto:bpruessm@users.sourceforge.net">Boris Pruessmann</a>
  * @version $Revision: 244 $  
  */
class MoveDeleteHook implements IMoveDeleteHook 
{
	// --------------------------------------------------------------------------- Private Variables
	
	/** Stores the Team provider to which the hook belongs. */
	private IP4TeamProvider provider;
	
	// ------------------------------------------------------------------------- Public Constructors
	
	/** Constructor for MoveDeleteHook.
	  *
	  * @param provider the provider. 
	  */
	public MoveDeleteHook(IP4TeamProvider provider)
	{
		this.provider = provider;	
	}
	
	// ------------------------------------------------------------------------------ Public Methods
	
	/** @see IMoveDeleteHook#deleteFile(IResourceTree, IFile, int, IProgressMonitor) */
	public boolean deleteFile(IResourceTree tree, IFile file, int flags, IProgressMonitor monitor) 
	{
		if (!provider.hasRemote(file))
		{
			return false;
		}
		
		monitor = Policy.monitorFor(monitor);
		monitor.beginTask("", 1500);
		monitor.subTask("");
		
		// If the local file is opened for ... we have to revert it
		IResource[] resources = new IResource[]{ file }; 
		if (provider.isCheckedOut(file))
		{
			try
			{
				IProgressMonitor subMonitor = 
					Policy.subMonitorFor(monitor, 500, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL);
				
				provider.revert(resources , IResource.DEPTH_ONE, null);
			}
			catch (PerforceException e)
			{
				tree.failed(e.getStatus());
				return false;
			}
		}
		else
		{		
			monitor.worked(500);
		}
				
		if (PerforceCorePlugin.getPlugin().getDeleteAffectsChangelist())
		{
			// If the file is now under perforce control we have to open it for 
			// delete.			
			if (provider.hasRemote(file))
			{
				try
				{
					boolean keepHistory = (flags & IResource.KEEP_HISTORY) != 0;
				
					// Add the file to the local history if requested by the user.
					if (keepHistory)
					{
						tree.addToLocalHistory(file);
					}
					monitor.worked(500);
					
					IProgressMonitor subMonitor = 
						Policy.subMonitorFor(
							monitor, 500, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL);
														
					provider.openForDelete(resources, IResource.DEPTH_ONE, subMonitor);
					
					return true;
				}
				catch (PerforceException e)
				{
					tree.failed(e.getStatus());
					return false;
				}
				finally
				{
					monitor.done();
				}
			}
			else
			{
				monitor.worked(1000);
			}
		}
		
		tree.standardDeleteFile(file, flags, Policy.subMonitorFor(monitor, 1000));
		return true;
	}

	/** @see IMoveDeleteHook#deleteFolder(IResourceTree, IFolder, int, IProgressMonitor) */
	public boolean deleteFolder(
		IResourceTree tree,	IFolder folder,	int flags, IProgressMonitor monitor) 
	{
		if (!provider.hasRemote(folder))
		{
			return false;
		}
		
		try
		{
			final List resourceList = new ArrayList();
			folder.accept(new IResourceVisitor() 
			{
				public boolean visit(IResource resource) 
				{
					if (resource.getType() == IResource.FILE)
					{
						resourceList.add(resource);
					}
					return true;
				}
			});
	
			monitor = Policy.monitorFor(monitor);
			monitor.beginTask("", resourceList.size() * 1000 + 1000);
			monitor.subTask("");
			
			// We lock the PerforceSynchronizer during the delete process to avoid
			// cache commits
			try
			{
				PerforceSynchronizer.getInstance().beginOperation(null);
				
				for (Iterator it = resourceList.iterator(); it.hasNext();)
				{
					IFile file = (IFile) it.next();
					
					IProgressMonitor subMonitor = Policy.subMonitorFor(monitor, 1000);
					deleteFile(tree, file, flags, subMonitor);
				}
			}
			finally
			{
				PerforceSynchronizer.getInstance().endOperation(null);
			}
			
			monitor.done();
			
			tree.standardDeleteFolder(folder, flags, monitor);
		}
		catch (PerforceException e)
		{
			tree.failed(e.getStatus());			
		}
		catch (CoreException e)
		{
			tree.failed(e.getStatus());
		}
			
		return false;
	}

	/** @see IMoveDeleteHook#deleteProject(IResourceTree, IProject, int, IProgressMonitor) */
	public boolean deleteProject(
		IResourceTree tree,
		IProject project,
		int updateFlags,
		IProgressMonitor monitor) 
	{
		return false;
	}

	/** @see IMoveDeleteHook#moveFile(IResourceTree, IFile, IFile, int, IProgressMonitor) */
	public boolean moveFile(
		IResourceTree tree,
		IFile source,
		IFile destination,
		int updateFlags,
		IProgressMonitor monitor) 
	{		
		refreshState(new IResource[]{ source, destination }, null);		
		if (!provider.hasRemote(source))
		{
			return false;
		}

		monitor.beginTask("", 1000);                                                   //$NON-NLS-1$
		monitor.subTask(source.getName());
		try
		{
			final IPerforceServer server = ((P4TeamProvider) provider).getPerforceServer();			
			if (server.moveFile(tree, source, destination, updateFlags, monitor))
			{
				tree.movedFile(source, destination);
				return true;			
			}
			else
			{
				return false;
			}
		}
		finally
		{			
			refreshState(new IResource[]{ source, destination }, null);
			PerforceCorePlugin.getPlugin().broadcastChangelistChanges();
			monitor.done();
		}
	}

	/** @see IMoveDeleteHook#moveFolder(IResourceTree, IFolder, IFolder, int, IProgressMonitor) */
	public boolean moveFolder(
		IResourceTree tree,
		IFolder source,
		IFolder destination,
		int updateFlags,
		IProgressMonitor monitor) 
	{
		try
		{
			PerforceSynchronizer.getInstance().beginOperation(null);

			refreshState(new IResource[]{ source, destination }, null);
			if (!provider.hasRemote(source))
			{
				return false;
			}
	
			try
			{
				final List resourceList = new ArrayList();			
				source.accept(new IResourceVisitor() 
				{
					public boolean visit(IResource resource) 
					{
						if (resource.getType() == IResource.FILE)
						{
							resourceList.add(resource);
						}
						return true;
					}
				});
							
				monitor = Policy.monitorFor(monitor);
				monitor.beginTask("", resourceList.size() * 1000 + 1000);                  //$NON-NLS-1$
				monitor.setTaskName("");                                                   //$NON-NLS-1$
				
				IPath sourcePath = source.getProjectRelativePath();			
				
				final IPerforceServer server = ((P4TeamProvider) provider).getPerforceServer();
				boolean ok = true;
				for (Iterator it = resourceList.iterator(); ok && it.hasNext();)
				{
					IFile file = (IFile) it.next();
					
					IPath filePath = file.getProjectRelativePath();
					
					int matchingSegments = filePath.matchingFirstSegments(sourcePath);
					IPath delta = filePath.removeFirstSegments(matchingSegments);
					
					IFile destinationFile = destination.getFile(delta);
					createFolder((IFolder) destinationFile.getParent());
					
					IProgressMonitor subMonitor = Policy.subMonitorFor(monitor, 1000);
					
					if (!server.moveFile(tree, file, destinationFile, updateFlags, monitor))
					{
						ok = false;
					}
				}
				
				if (ok)
				{
					deleteDirectory(source);
					tree.movedFolderSubtree(source, destination);
				}
			}
			finally
			{
				refreshState(new IResource[]{ source, destination }, null);
				PerforceCorePlugin.getPlugin().broadcastChangelistChanges();
				
				IProgressMonitor subMonitor = Policy.subMonitorFor(monitor, 1000);
				try
				{
					PerforceSynchronizer.getInstance().endOperation(subMonitor);
				}
				catch (PerforceException e)
				{
					PerforceCorePlugin.log(e.getStatus());
				}
				monitor.done();
			}
			
			return true;
		}
		catch (CoreException e)
		{
			tree.failed(e.getStatus());
		}
		return false;
	}

	/** @see IMoveDeleteHook#moveProject(
	  *          IResourceTree, IProject, IProjectDescription, int, IProgressMonitor)
	  */
	public boolean moveProject(
		IResourceTree tree,
		IProject source,
		IProjectDescription description,
		int updateFlags,
		IProgressMonitor monitor) 
	{
		return false;
	}

	// ----------------------------------------------------------------------------- Private Methods

	/** Called to create the folder. 
	  * 
	  * @param folderHandle the folder handle.
	  */
	private void createFolder(IFolder folderHandle) 
	{
		File folder = new File(folderHandle.getLocation().toOSString());
		folder.mkdirs();
	}	

	private void deleteDirectory(IFolder folderHandle) 
	{
		File folder = new File(folderHandle.getLocation().toOSString());
		folder.delete();
	}

	/** @see P4TeamProvider#refreshState(IResource[], int, IProgressMonitor) */
	private void refreshState(IResource[] resources, IProgressMonitor monitor)
	{
		P4TeamProvider provider = 
			(P4TeamProvider) RepositoryProvider.getProvider(resources[0].getProject());
	
		try
		{		
			provider.refreshState(resources, IResource.DEPTH_INFINITE, monitor);
		}
		catch (PerforceException e)
		{
			PerforceCorePlugin.log(e.getStatus());
		}
	}	
}

